package org.johnnei.engine.render;

import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glColor3f;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glTexCoord2f;
import static org.lwjgl.opengl.GL11.glVertex2f;
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
import static org.lwjgl.opengl.GL13.glActiveTexture;

public class TextRenderer {

	private static TextRenderer instance = new TextRenderer();
	
	/**
	 * The texture of the font
	 */
	private Texture fontTexture;
	/**
	 * The padding between 2 characters
	 */
	public static final int PADDING = 1;
	
	/**
	 * Information about the font to render everything smoothly
	 */
	private final short[][] fontInfo = new short[][] {
		  //{   x,  y,  w,  h}, // Char
			{   0,  0, 10, 15}, // SPACE
			{  12,  0,  3, 12}, // !
			{  19,  0,  7,  4}, // "
			{  26,  0,  9, 12}, // #
			{  37,  0,  7, 13}, // $
			{  45,  0,  9, 12}, // %
			{  54,  0, 10, 12}, // &
			{  66,  0,  3,  5}, // '
			{  74,  0,  7, 14}, // (
			{  82,  0,  6, 14}, // )
			{  91,  0,  8,  8}, // *
			{ 100,  0,  8, 12}, // +
			{ 111,  0,  4, 14}, // ,
			{ 119,  0,  6,  8}, // -
			{ 129,  0,  4, 12}, // .
			{ 136,  0,  9, 14}, // /
			{ 145,  0,  9, 12}, // 0
			{ 155,  0,  8, 12}, // 1
			{ 163,  0,  8, 12}, // 2
			{ 173,  0,  7, 12}, // 3
			{ 181,  0,  8, 12}, // 4
			{ 191,  0,  7, 12}, // 5
			{ 200,  0,  8, 12}, // 6
			{ 209,  0,  7, 12}, // 7
			{ 218,  0,  7, 12}, // 8
			{ 227,  0,  8, 12}, // 9
			{ 238,  0,  3, 12}, // :
			{ 247,  0,  3, 14}, // ;
			{   0, 16,  9,  9}, // <
			{   9, 16,  9,  7}, // =
			{  18, 16,  9,  9}, // >
			{  28, 16,  8, 12}, // ?
			{  36, 16,  9, 12}, // @
			{  45, 16, 10, 12}, // A
			{  55, 16,  8, 12}, // B
			{  64, 16,  8, 12}, // C
			{  73, 16,  8, 12}, // D
			{  82, 16,  8, 12}, // E
			{  92, 16,  7, 12}, // F
			{  99, 16,  9, 12}, // G
			{ 109, 16,  8, 12}, // H
			{ 118, 16,  8, 12}, // I
			{ 127, 16,  7, 12}, // J
			{ 136, 16,  9, 12}, // K
			{ 146, 16,  7, 12}, // L
			{ 154, 16,  8, 12}, // M
			{ 163, 16,  8, 12}, // N
			{ 172, 16,  9, 12}, // O
			{ 182, 16,  8, 12}, // P
			{ 190, 16,  9, 14}, // Q
			{ 200, 16,  8, 12}, // R
			{ 209, 16,  8, 12}, // S
			{ 217, 16,  9, 12}, // T
			{ 227, 16,  7, 12}, // U
			{ 235, 16,  9, 12}, // V
			{ 244, 16,  9, 12}, // W
			{   0, 31,  9, 12}, // X
			{   9, 31,  9, 12}, // Y
			{  18, 31,  9, 12}, // Z
			{  30, 31,  5, 14}, // [
			{  36, 31,  9, 14}, // \
			{  46, 31,  5, 14}, // ]
			{  54, 31,  9, 10}, // ^
			{  63, 31, 10, 13}, // _
			{  75, 31,  4,  3}, // `
			{  82, 32,  8, 12}, // a
			{  91, 32,  8, 12}, // b
			{ 100, 32,  8, 12}, // c
			{ 109, 32,  8, 12}, // d
			{ 118, 32,  8, 12}, // e
			{ 127, 32,  9, 12}, // f
			{ 136, 32,  8, 15}, // g
			{ 145, 32,  8, 12}, // h
			{ 154, 32,  6, 12}, // i 
			{ 163, 32,  7, 15}, // j
			{ 173, 32,  8, 12}, // k 
			{ 182, 32,  5, 12}, // l
			{ 190, 32,  9, 12}, // m
			{ 200, 32,  7, 12}, // n
			{ 208, 32,  9, 12}, // o
			{ 218, 32,  8, 15}, // p
			{ 226, 32,  8, 15}, // q
			{ 237, 32,  7, 12}, // r
			{ 245, 32,  7, 12}, // s
			{   0, 48,  9, 12}, // t
			{  10, 48,  7, 12}, // u
			{  18, 48,  9, 12}, // v
			{  27, 48,  9, 12}, // w
			{  36, 48,  9, 12}, // x
			{  45, 48,  9, 15}, // y
			{  55, 48,  8, 12}, // z
			{  64, 48,  7, 14}, // {
			{  76, 48,  2, 14}, // |
			{  82, 48,  7, 14}, // }
			{  91, 48,  8, 10}  // ~
	};

	private TextRenderer() {
		fontTexture = new Texture("/res/font.png");
		fontTexture.setSizeLimit(true);
		for(int i = 0; i < fontInfo.length; i++) {
			fontTexture.addSubTexture(fontInfo[i][0], fontInfo[i][1], fontInfo[i][2], fontInfo[i][3]);
		}
	}

	/**
	 * Draws a string with the center at the given location. The used color is white
	 * @param x The x-Coordinate
	 * @param y The y-Coordinate
	 * @param text The String to be drawn
	 */
	public static void drawCentered(float x, float y, String text) {
		drawCentered(x, y, text, null);
	}
	
	/**
	 * Draws a string with the center at the given location
	 * @param x The x-Coordinate
	 * @param y The y-Coordinate
	 * @param text The String to be drawn
	 * @param color The Color in which this string should be drawn (Null = white)
	 */
	public static void drawCentered(float x, float y, String text, ColorHelper color) {
		x -= (text.length() * 4.5F);
		draw(x, y, text, color);
	}
	
	/**
	 * Draws a string with the left-top at the given location
	 * @param x The x-Coordinate
	 * @param y The y-Coordinate
	 * @param text The String to be drawn
	 */
	public static void draw(float x, float y, String text) {
		draw(x, y, text, null);
	}

	/**
	 * Draws a string with the left-top at the given location
	 * @param x The x-Coordinate
	 * @param y The y-Coordinate
	 * @param text The String to be drawn
	 * @param color The Color in which this string should be drawn (Null = white)
	 */
	public static void draw(float x, float y, String text, ColorHelper color) {
		instance.drawText(x, y, text, (color != null) ? color : new ColorHelper(0xff, 0xff, 0xff));
	}
	
	/**
	 * Draws the text on the given location
	 * @param x the x coordinate
	 * @param y the y coordinate
	 * @param text the text to be drawn
	 * @param color the color to use
	 */
	public void drawText(float x, float y, String text, ColorHelper color) {
		int xOffset = 0;
		
		glEnable(GL_TEXTURE_2D);
		glActiveTexture(GL_TEXTURE0);
		fontTexture.bind();
		
		glBegin(GL_QUADS);
		glColor3f(color.getR(), color.getG(), color.getB());
		for(char c : text.toCharArray()) {
			float[][] textureInfo = fontTexture.getSubTextureInfo(c - 32);
			int width = fontInfo[c - 32][2];
			int height = fontInfo[c - 32][3];
			
			glTexCoord2f(textureInfo[0][0], textureInfo[0][1]);
			glVertex2f(x + xOffset, y + height);
			
			glTexCoord2f(textureInfo[1][0], textureInfo[1][1]);
			glVertex2f(x + xOffset + width, y + height);
			
			glTexCoord2f(textureInfo[2][0], textureInfo[2][1]);
			glVertex2f(x + xOffset + width, y);
			
			glTexCoord2f(textureInfo[3][0], textureInfo[3][1]);
			glVertex2f(x + xOffset, y);
			
			xOffset += PADDING + width;
		}
		glEnd();
		glDisable(GL_TEXTURE_2D);
	}
	
	public void cleanupResources() {
		fontTexture.delete();
	}
	
	public int getTextWidthForString(String s) {
		int length = 0;
		for(char c : s.toCharArray()) {
			length += PADDING + fontInfo[c - 32][2];
		}
		return length;
	}
	
	public static int getTextWidth(String s) {
		return instance.getTextWidthForString(s);
	}
	
	public static int getTextHeight() {
		return 16;
	}
	
	public static void cleanup() {
		instance.cleanupResources();
	}

}
